#include "PrecompiledHeader.h"
#include "IRenderPhase.h"

/*--------------------------------------------------------------------------*/

IRenderPhase::IRenderPhase() :
	m_sceneCamera(NULL),
	m_inputPPFX(NULL),
	m_outputPPFX(NULL),
	m_lastFBO(NULL),
	m_initialized(false),
	m_priority(0),
	m_camarg(0)
{
	this->m_taskVectorEnabled = true;
}

/*--------------------------------------------------------------------------*/

IRenderPhase::~IRenderPhase()
{
	for (auto iter = this->m_postprocess.begin(); iter != this->m_postprocess.end(); ++iter)
	{
		delete(*iter);
	}

	for (auto iter = this->m_subphases.begin(); iter != this->m_subphases.end(); ++iter)
	{
		if ((*iter)->m_fbo)
		{
			ROpenGL::DeleteFramebuffer(&(*iter)->m_fbo);
		}

		delete *iter;
	}

	if (this->m_lastFBO)
	{
		ROpenGL::DeleteFramebuffer(&this->m_lastFBO);
	}
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::Initialize(GLTexture* output)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SortTasks(uint fboid)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::AddTask(RenderTask* task)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::AddTaskToVector(uint fboid, RenderTask* task)
{
	if (!this->m_taskVectorEnabled)
	{
		return;
	}

	this->m_subphases[fboid]->Lock(0);

	if (task->GetFlags() & ERenderTaskFlag::SortByDist || task->GetFlags() & ERenderTaskFlag::InvSortByDist)
	{
		float distance = glm::distance(glm::vec3(task->GetObject()->GetModelMatrix()[3][0],
		                                         task->GetObject()->GetModelMatrix()[3][1],
		                                         task->GetObject()->GetModelMatrix()[3][2]) + this->m_sceneCamera->GetPosition(),
		                               glm::vec3(0.0f));

		task->SetPriority(distance);
	}

	this->m_subphases[fboid]->m_rendertasks.push_back(task);
	this->m_subphases[fboid]->Unlock(0);
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PreFramebufferBind(uint fboid)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PostFramebufferBind(uint fboid)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PreTaskRender(uint fboid, RenderTask* task)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PostTaskRender(uint fboid, RenderTask* task)
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::BindMaterial(RenderTask* task)
{
	if ((task->GetFlags() & ERenderTaskFlag::NoMaterial) == 0)
	{
		g_Material->BindMaterial(task->GetMaterial(), EMaterialTechnique::Default);
	}
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::BindCamera()
{
	if (this->m_sceneCamera)
	{
		ICamera* camera = this->m_sceneCamera;

		if (camera)
		{
			g_Renderer->SetViewMatrix(camera->GetViewMatrix());
			g_Renderer->SetProjectionMatrix(camera->GetProjectionMatrix());
			g_Renderer->UpdateCameraMatrices();
		}
	}
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PrePostprocess()
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PostPostprocess()
{

}

/*--------------------------------------------------------------------------*/

uint IRenderPhase::GetNumFramebuffers()
{
	return this->m_subphases.size();
}

/*--------------------------------------------------------------------------*/

SubRenderPhase* IRenderPhase::CreateSubRenderPhase()
{
	this->m_subphases.push_back(new SubRenderPhase());
	return this->m_subphases.back();
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetPostprocessOutput(GLTexture* texture)
{
	if (texture)
	{
		this->m_lastFBO = ROpenGL::GenFramebuffer("LastFBO_" + this->m_name);
		ROpenGL::SetFramebufferColorAttachment(this->m_lastFBO, 0, texture);
		ROpenGL::ValidateFramebuffer(this->m_lastFBO);

		this->m_outputPPFX = texture;
	}
	else
	{
		this->m_lastFBO = NULL;
		this->m_outputPPFX = NULL;
	}
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetPostprocessInput(GLTexture* texture)
{
	this->m_inputPPFX = texture;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::AddPostprocess(IPostprocess* postprocess)
{
	this->m_postprocess.push_back(postprocess);
}

/*--------------------------------------------------------------------------*/

std::vector<RenderTask*>& IRenderPhase::GetRenderTasks(uint fboid)
{
	return this->m_subphases[fboid]->m_rendertasks;
}

/*--------------------------------------------------------------------------*/

bool IRenderPhase::IsInitialized() const
{
	return this->m_initialized;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::Process()
{
	this->LockTaskVectors();
	this->PrePhase();
	this->BindCamera();

	/*----Tasks-----------------------------------------------------------------*/
	uint numFramebuffers = this->GetNumFramebuffers();

	for (uint i = 0; i < numFramebuffers; ++i)
	{
		SubRenderPhase* sub = this->m_subphases[i];
		ViaMain::Timestamp("Processing SubRenderPhase %d", i);

		this->SortTasks(i);

		this->PreFramebufferBind(i);
		ROpenGL::BindFramebuffer(sub->m_fbo);
		this->PostFramebufferBind(i);

		/*--------------------------------------------------------------------------*/

		for (auto task = sub->m_rendertasks.begin(); task != sub->m_rendertasks.end(); ++task)
		{
			RenderTask* rt = (*task);
			g_Renderer->SetModelMatrix(rt->GetObject()->GetModelMatrix());
			g_Renderer->UpdateModelMatrices();

			this->BindMaterial(rt);

			this->PreTaskRender(i, rt);
			rt->GetObject()->OnPreRender(rt);
			rt->GetObject()->OnRender(rt, this->m_camarg);
			rt->GetObject()->OnPostRender(rt);
			this->PostTaskRender(i, rt);
		}

		/*--------------------------------------------------------------------------*/
		sub->m_rendertasks.clear();
	}

	/*----Postprocesses---------------------------------------------------------*/
	this->PrePostprocess();
	uint numPPFXPhases = this->m_postprocess.size(); //last FBO

	for (uint i = 0; i < numPPFXPhases; ++i)
	{
		this->m_postprocess[i]->BindMaterial();

		if (i == 0) // pierwszy
		{
			if (this->m_inputPPFX)
			{
				ROpenGL::SetSampler("ppfxBuffer", this->m_inputPPFX);
			}
			else
			{
				ROpenGL::SetSampler("ppfxBuffer", g_Renderer->GetTextureBuffer(ETexture::PPFX_B));
			}
		}
		else
		{
			if (i % 2)
			{
				ROpenGL::SetSampler("ppfxBuffer", g_Renderer->GetTextureBuffer(ETexture::PPFX_A));
			}
			else
			{
				ROpenGL::SetSampler("ppfxBuffer", g_Renderer->GetTextureBuffer(ETexture::PPFX_B));
			}
		}

		if (i + 1 != numPPFXPhases) // nie ostatnia faza
		{
			if (i % 2)
			{
				g_Renderer->BindPPFXB();
			}
			else
			{
				g_Renderer->BindPPFXA();
			}
		}
		else
		{
			ROpenGL::BindFramebuffer(this->m_lastFBO);
		}

		ROpenGL::ClearColorBuffer();

		this->m_postprocess[i]->UploadParameters();
		this->m_postprocess[i]->ApplyPostprocess();
	}

	this->PostPostprocess();
	this->PostPhase();
	this->UnlockTaskVectors();
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetPriority(uint priority)
{
	this->m_priority = priority;
}

/*--------------------------------------------------------------------------*/

uint IRenderPhase::GetPriority() const
{
	return this->m_priority;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetCamera(ICamera* camera, uint camarg)
{
	this->m_sceneCamera = camera;
	this->m_camarg = camarg;
}

/*--------------------------------------------------------------------------*/

ICamera* IRenderPhase::GetCamera()
{
	return this->m_sceneCamera;
}

/*--------------------------------------------------------------------------*/

SceneInstance* IRenderPhase::GetScene()
{
	return this->m_sceneInstance;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetScene(SceneInstance* scene)
{
	this->m_sceneInstance = scene;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::SetName(const std::string& name)
{
	this->m_name = name;
}

/*--------------------------------------------------------------------------*/

const std::string& IRenderPhase::GetName()
{
	return this->m_name;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PrePhase()
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::PostPhase()
{

}

/*--------------------------------------------------------------------------*/

void IRenderPhase::LockTaskVectors()
{
	this->m_taskVectorEnabled = false;
}

/*--------------------------------------------------------------------------*/

void IRenderPhase::UnlockTaskVectors()
{
	this->m_taskVectorEnabled = true;
}

/*--------------------------------------------------------------------------*/
